import type { RequestHandler } from 'express';
import { sendError, sendSuccess } from '../utils/response';
import fsProm from 'fs/promises';
import fs from 'fs';
import path from 'path';
import os from 'os';
import compressing from 'compressing';
import { PassThrough } from 'stream';
import multer from 'multer';
import webUIFontUploader from '../uploader/webui_font';
import diskUploader from '../uploader/disk';
import { WebUiConfig, getInitialWebUiToken, webUiPathWrapper } from '@/napcat-webui-backend/index';

const isWindows = os.platform() === 'win32';

// 安全地从查询参数中提取字符串值，防止类型混淆
const getQueryStringParam = (param: any): string => {
  if (Array.isArray(param)) {
    return String(param[0] || '');
  }
  return String(param || '');
};

// 获取系统根目录列表（Windows返回盘符列表，其他系统返回['/']）
const getRootDirs = async (): Promise<string[]> => {
  if (!isWindows) return ['/'];

  // Windows 驱动器字母 (A-Z)
  const drives: string[] = [];
  for (let i = 65; i <= 90; i++) {
    const driveLetter = String.fromCharCode(i);
    try {
      await fsProm.access(`${driveLetter}:\\`);
      drives.push(`${driveLetter}:`);
    } catch {
      // 如果驱动器不存在或无法访问，跳过
      continue;
    }
  }
  return drives.length > 0 ? drives : ['C:'];
};

// 规范化路径并进行安全验证
const normalizePath = (inputPath: string): string => {
  if (!inputPath) {
    // 对于空路径，Windows返回用户主目录，其他系统返回根目录
    return isWindows ? process.env['USERPROFILE'] || 'C:\\' : '/';
  }

  // 对输入路径进行清理，移除潜在的危险字符
  // eslint-disable-next-line no-control-regex
  const cleanedPath = inputPath.replace(/[\x01-\x1f\x7f]/g, ''); // 移除控制字符

  // 如果是Windows且输入为纯盘符（可能带或不带斜杠），统一返回 "X:\"
  if (isWindows && /^[A-Z]:[\\/]*$/i.test(cleanedPath)) {
    return cleanedPath.slice(0, 2) + '\\';
  }

  // 安全验证：检查是否包含危险的路径遍历模式（在规范化之前）
  if (containsPathTraversal(cleanedPath)) {
    throw new Error('Invalid path: path traversal detected');
  }

  // 进行路径规范化
  const normalized = path.resolve(cleanedPath);

  // 再次检查规范化后的路径，确保没有绕过安全检查
  if (containsPathTraversal(normalized)) {
    throw new Error('Invalid path: path traversal detected after normalization');
  }

  // 额外安全检查：确保规范化后的路径不包含连续的路径分隔符
  const finalPath = normalized.replace(/[\\/]+/g, path.sep);

  return finalPath;
};

// 检查路径是否包含路径遍历攻击模式
const containsPathTraversal = (inputPath: string): boolean => {
  // 对输入进行URL解码，防止编码绕过
  let decodedPath = inputPath;
  try {
    decodedPath = decodeURIComponent(inputPath);
  } catch {
    // 如果解码失败，使用原始路径
  }

  // 将路径统一为正斜杠格式进行检查
  const normalizedForCheck = decodedPath.replace(/\\/g, '/');

  // 检查危险模式 - 更全面的路径遍历检测
  const dangerousPatterns = [
    /\.\.\//, // ../ 模式
    /\/\.\./, // /.. 模式
    /^\.\./, // 以.. 开头
    /\.\.$/, // 以.. 结尾
    /\.\.\\/, // ..\ 模式（Windows）
    /\\\.\./, // \.. 模式（Windows）
    /%2e%2e/i, // URL编码的..
    /%252e%252e/i, // 双重URL编码的..
    // eslint-disable-next-line no-control-regex
    /\.\.\x00/, // null字节攻击
    /\0/, // null字节
  ];

  return dangerousPatterns.some(pattern => pattern.test(normalizedForCheck));
};

interface FileInfo {
  name: string;
  isDirectory: boolean;
  size: number;
  mtime: Date;
}

// 添加系统文件黑名单
const SYSTEM_FILES = new Set(['pagefile.sys', 'swapfile.sys', 'hiberfil.sys', 'System Volume Information']);

// 检查是否为WebUI配置文件
const isWebUIConfigFile = (filePath: string): boolean => {
  // 先用字符串快速筛选
  if (!filePath.includes('webui.json')) {
    return false;
  }

  // 进入更严格的路径判断 - 统一路径分隔符为 /
  const webUIConfigPath = path.resolve(webUiPathWrapper.configPath, 'webui.json').replace(/\\/g, '/');
  const targetPath = path.resolve(filePath).replace(/\\/g, '/');

  // 统一分隔符后进行路径比较
  return targetPath === webUIConfigPath;
};

// WebUI配置文件脱敏处理
const sanitizeWebUIConfig = (content: string): string => {
  try {
    const config = JSON.parse(content);
    if (config.token) {
      config.token = '******';
    }
    return JSON.stringify(config, null, 4);
  } catch {
    // 如果解析失败，返回原内容
    return content;
  }
};

// 检查同类型的文件或目录是否存在
const checkSameTypeExists = async (pathToCheck: string, isDirectory: boolean): Promise<boolean> => {
  try {
    const stat = await fsProm.stat(pathToCheck);
    // 只有当类型相同时才认为是冲突
    return stat.isDirectory() === isDirectory;
  } catch {
    return false;
  }
};

// 获取目录内容
export const ListFilesHandler: RequestHandler = async (req, res) => {
  try {
    const requestPath = getQueryStringParam(req.query['path']) || (isWindows ? process.env['USERPROFILE'] || 'C:\\' : '/');

    let normalizedPath: string;
    try {
      normalizedPath = normalizePath(requestPath);
    } catch (_pathError) {
      return sendError(res, '无效的文件路径');
    }

    const onlyDirectory = req.query['onlyDirectory'] === 'true';

    // 如果是根路径且在Windows系统上，返回盘符列表
    if (isWindows && (!requestPath || requestPath === '/' || requestPath === '\\')) {
      const drives = await getRootDirs();
      const driveInfos: FileInfo[] = await Promise.all(
        drives.map(async (drive) => {
          try {
            const stat = await fsProm.stat(`${drive}\\`);
            return {
              name: drive,
              isDirectory: true,
              size: 0,
              mtime: stat.mtime,
            };
          } catch {
            return {
              name: drive,
              isDirectory: true,
              size: 0,
              mtime: new Date(),
            };
          }
        })
      );
      return sendSuccess(res, driveInfos);
    }

    const files = await fsProm.readdir(normalizedPath);
    let fileInfos: FileInfo[] = [];

    for (const file of files) {
      // 跳过系统文件
      if (SYSTEM_FILES.has(file)) continue;

      try {
        const fullPath = path.join(normalizedPath, file);
        const stat = await fsProm.stat(fullPath);
        fileInfos.push({
          name: file,
          isDirectory: stat.isDirectory(),
          size: stat.size,
          mtime: stat.mtime,
        });
      } catch (_error) {
        // 忽略无法访问的文件
        // console.warn(`无法访问文件 ${file}:`, error);
        continue;
      }
    }

    // 如果请求参数 onlyDirectory 为 true，则只返回目录信息
    if (onlyDirectory) {
      fileInfos = fileInfos.filter((info) => info.isDirectory);
    }

    return sendSuccess(res, fileInfos);
  } catch (_error) {
    console.error('读取目录失败:', _error);
    return sendError(res, '读取目录失败');
  }
};

// 创建目录
export const CreateDirHandler: RequestHandler = async (req, res) => {
  try {
    const { path: dirPath } = req.body;

    let normalizedPath: string;
    try {
      normalizedPath = normalizePath(dirPath);
    } catch (_pathError) {
      return sendError(res, '无效的文件路径');
    }

    // 额外安全检查：确保路径是绝对路径
    if (!path.isAbsolute(normalizedPath)) {
      return sendError(res, '路径必须是绝对路径');
    }

    // 检查是否已存在同类型（目录）
    if (await checkSameTypeExists(normalizedPath, true)) {
      return sendError(res, '同名目录已存在');
    }

    await fsProm.mkdir(normalizedPath, { recursive: true });
    return sendSuccess(res, true);
  } catch (_error) {
    return sendError(res, '创建目录失败');
  }
};

// 删除文件/目录
export const DeleteHandler: RequestHandler = async (req, res) => {
  try {
    const { path: targetPath } = req.body;

    let normalizedPath: string;
    try {
      normalizedPath = normalizePath(targetPath);
    } catch (_pathError) {
      return sendError(res, '无效的文件路径');
    }

    // 额外安全检查：确保路径是绝对路径
    if (!path.isAbsolute(normalizedPath)) {
      return sendError(res, '路径必须是绝对路径');
    }

    const stat = await fsProm.stat(normalizedPath);
    if (stat.isDirectory()) {
      await fsProm.rm(normalizedPath, { recursive: true });
    } else {
      await fsProm.unlink(normalizedPath);
    }
    return sendSuccess(res, true);
  } catch (_error) {
    return sendError(res, '删除失败');
  }
};

// 批量删除文件/目录
export const BatchDeleteHandler: RequestHandler = async (req, res) => {
  try {
    const { paths } = req.body;
    for (const targetPath of paths) {
      let normalizedPath: string;
      try {
        normalizedPath = normalizePath(targetPath);
      } catch (_pathError) {
        return sendError(res, '无效的文件路径');
      }

      // 额外安全检查：确保路径是绝对路径
      if (!path.isAbsolute(normalizedPath)) {
        return sendError(res, '路径必须是绝对路径');
      }

      const stat = await fsProm.stat(normalizedPath);
      if (stat.isDirectory()) {
        await fsProm.rm(normalizedPath, { recursive: true });
      } else {
        await fsProm.unlink(normalizedPath);
      }
    }
    return sendSuccess(res, true);
  } catch (_error) {
    return sendError(res, '批量删除失败');
  }
};

// 读取文件内容
export const ReadFileHandler: RequestHandler = async (req, res) => {
  try {
    let filePath: string;
    try {
      filePath = normalizePath(getQueryStringParam(req.query['path']));
    } catch (_pathError) {
      return sendError(res, '无效的文件路径');
    }

    // 额外安全检查：确保路径是绝对路径
    if (!path.isAbsolute(filePath)) {
      return sendError(res, '路径必须是绝对路径');
    }

    let content = await fsProm.readFile(filePath, 'utf-8');

    // 如果是WebUI配置文件，对token进行脱敏处理
    if (isWebUIConfigFile(filePath)) {
      content = sanitizeWebUIConfig(content);
    }

    return sendSuccess(res, content);
  } catch (_error) {
    return sendError(res, '读取文件失败');
  }
};

// 写入文件内容
export const WriteFileHandler: RequestHandler = async (req, res) => {
  try {
    const { path: filePath, content } = req.body;

    // 安全的路径规范化，如果检测到路径遍历攻击会抛出异常
    let normalizedPath: string;
    try {
      normalizedPath = normalizePath(filePath);
    } catch (_pathError) {
      return sendError(res, '无效的文件路径');
    }

    // 额外安全检查：确保路径是绝对路径
    if (!path.isAbsolute(normalizedPath)) {
      return sendError(res, '路径必须是绝对路径');
    }

    let finalContent = content;

    // 检查是否为WebUI配置文件
    if (isWebUIConfigFile(normalizedPath)) {
      try {
        // 解析要写入的配置
        const configToWrite = JSON.parse(content);
        // 获取内存中的token，覆盖前端传来的token
        const memoryToken = getInitialWebUiToken();
        if (memoryToken) {
          configToWrite.token = memoryToken;
          finalContent = JSON.stringify(configToWrite, null, 4);
        }
      } catch (_e) {
        // 如果解析失败 说明不符合json格式 不允许写入
        return sendError(res, '写入的WebUI配置文件内容格式错误，必须是合法的JSON');
      }
    }

    await fsProm.writeFile(normalizedPath, finalContent, 'utf-8');
    return sendSuccess(res, true);
  } catch (_error) {
    return sendError(res, '写入文件失败');
  }
};

// 创建新文件
export const CreateFileHandler: RequestHandler = async (req, res) => {
  try {
    const { path: filePath } = req.body;

    let normalizedPath: string;
    try {
      normalizedPath = normalizePath(filePath);
    } catch (_pathError) {
      return sendError(res, '无效的文件路径');
    }

    // 额外安全检查：确保路径是绝对路径
    if (!path.isAbsolute(normalizedPath)) {
      return sendError(res, '路径必须是绝对路径');
    }

    // 检查是否已存在同类型（文件）
    if (await checkSameTypeExists(normalizedPath, false)) {
      return sendError(res, '同名文件已存在');
    }

    await fsProm.writeFile(normalizedPath, '', 'utf-8');
    return sendSuccess(res, true);
  } catch (_error) {
    return sendError(res, '创建文件失败');
  }
};

// 重命名文件/目录
export const RenameHandler: RequestHandler = async (req, res) => {
  try {
    const { oldPath, newPath } = req.body;

    let normalizedOldPath: string;
    let normalizedNewPath: string;
    try {
      normalizedOldPath = normalizePath(oldPath);
      normalizedNewPath = normalizePath(newPath);
    } catch (_pathError) {
      return sendError(res, '无效的文件路径');
    }

    // 额外安全检查：确保路径是绝对路径
    if (!path.isAbsolute(normalizedOldPath) || !path.isAbsolute(normalizedNewPath)) {
      return sendError(res, '路径必须是绝对路径');
    }

    await fsProm.rename(normalizedOldPath, normalizedNewPath);
    return sendSuccess(res, true);
  } catch (_error) {
    return sendError(res, '重命名失败');
  }
};

// 移动文件/目录
export const MoveHandler: RequestHandler = async (req, res) => {
  try {
    const { sourcePath, targetPath } = req.body;

    let normalizedSourcePath: string;
    let normalizedTargetPath: string;
    try {
      normalizedSourcePath = normalizePath(sourcePath);
      normalizedTargetPath = normalizePath(targetPath);
    } catch (_pathError) {
      return sendError(res, '无效的文件路径');
    }

    // 额外安全检查：确保路径是绝对路径
    if (!path.isAbsolute(normalizedSourcePath) || !path.isAbsolute(normalizedTargetPath)) {
      return sendError(res, '路径必须是绝对路径');
    }

    await fsProm.rename(normalizedSourcePath, normalizedTargetPath);
    return sendSuccess(res, true);
  } catch (_error) {
    return sendError(res, '移动失败');
  }
};

// 批量移动
export const BatchMoveHandler: RequestHandler = async (req, res) => {
  try {
    const { items } = req.body;
    for (const { sourcePath, targetPath } of items) {
      let normalizedSourcePath: string;
      let normalizedTargetPath: string;
      try {
        normalizedSourcePath = normalizePath(sourcePath);
        normalizedTargetPath = normalizePath(targetPath);
      } catch (_pathError) {
        return sendError(res, '无效的文件路径');
      }

      // 额外安全检查：确保路径是绝对路径
      if (!path.isAbsolute(normalizedSourcePath) || !path.isAbsolute(normalizedTargetPath)) {
        return sendError(res, '路径必须是绝对路径');
      }

      await fsProm.rename(normalizedSourcePath, normalizedTargetPath);
    }
    return sendSuccess(res, true);
  } catch (_error) {
    return sendError(res, '批量移动失败');
  }
};

// 新增：文件下载处理方法（注意流式传输，不将整个文件读入内存）
export const DownloadHandler: RequestHandler = async (req, res) => {
  try {
    let filePath: string;
    try {
      filePath = normalizePath(getQueryStringParam(req.query['path']));
    } catch (_pathError) {
      return sendError(res, '无效的文件路径');
    }

    if (!filePath) {
      return sendError(res, '参数错误');
    }

    // 额外安全检查：确保路径是绝对路径
    if (!path.isAbsolute(filePath)) {
      return sendError(res, '路径必须是绝对路径');
    }

    const stat = await fsProm.stat(filePath);

    res.setHeader('Content-Type', 'application/octet-stream');
    let filename = path.basename(filePath);
    if (stat.isDirectory()) {
      filename = path.basename(filePath) + '.zip';
      res.setHeader('Content-Disposition', `attachment; filename*=UTF-8''${encodeURIComponent(filename)}`);
      const zipStream = new PassThrough();
      compressing.zip.compressDir(filePath, zipStream as unknown as fs.WriteStream).catch((err) => {
        console.error('压缩目录失败:', err);
        res.end();
      });
      zipStream.pipe(res);
      return;
    }
    res.setHeader('Content-Length', stat.size);
    res.setHeader('Content-Disposition', `attachment; filename*=UTF-8''${encodeURIComponent(filename)}`);
    const stream = fs.createReadStream(filePath);
    stream.pipe(res);
  } catch (_error) {
    return sendError(res, '下载失败');
  }
};

// 批量下载：将多个文件/目录打包为 zip 文件下载
export const BatchDownloadHandler: RequestHandler = async (req, res) => {
  try {
    const { paths } = req.body as { paths: string[]; };
    if (!paths || !Array.isArray(paths) || paths.length === 0) {
      return sendError(res, '参数错误');
    }
    res.setHeader('Content-Type', 'application/octet-stream');
    res.setHeader('Content-Disposition', 'attachment; filename=files.zip');

    const zipStream = new compressing.zip.Stream();
    // 修改：根据文件类型设置 relativePath
    for (const filePath of paths) {
      let normalizedPath: string;
      try {
        normalizedPath = normalizePath(filePath);
      } catch (_pathError) {
        return sendError(res, '无效的文件路径');
      }

      // 额外安全检查：确保规范化后的路径是绝对路径
      if (!path.isAbsolute(normalizedPath)) {
        return sendError(res, '路径必须是绝对路径');
      }

      const stat = await fsProm.stat(normalizedPath);
      if (stat.isDirectory()) {
        zipStream.addEntry(normalizedPath, { relativePath: '' });
      } else {
        // 确保相对路径只使用文件名，防止路径遍历
        const safeName = path.basename(normalizedPath);
        zipStream.addEntry(normalizedPath, { relativePath: safeName });
      }
    }
    zipStream.pipe(res);
    res.on('finish', () => {
      zipStream.destroy();
    });
  } catch (_error) {
    return sendError(res, '下载失败');
  }
};

// 修改上传处理方法
export const UploadHandler: RequestHandler = async (req, res) => {
  try {
    await diskUploader(req, res);
    return sendSuccess(res, true, '文件上传成功', true);
  } catch (_error) {
    let errorMessage = '文件上传失败';

    if (_error instanceof multer.MulterError) {
      switch (_error.code) {
        case 'LIMIT_FILE_SIZE':
          errorMessage = '文件大小超过限制（40MB）';
          break;
        case 'LIMIT_UNEXPECTED_FILE':
          errorMessage = '无效的文件上传字段';
          break;
        default:
          errorMessage = `上传错误: ${_error.message}`;
      }
    } else if (_error instanceof Error) {
      errorMessage = _error.message;
    }
    return sendError(res, errorMessage, true);
  }
};

// 上传WebUI字体文件处理方法
export const UploadWebUIFontHandler: RequestHandler = async (req, res) => {
  try {
    await webUIFontUploader(req, res);
    return sendSuccess(res, true, '字体文件上传成功', true);
  } catch (error) {
    let errorMessage = '字体文件上传失败';

    if (error instanceof multer.MulterError) {
      switch (error.code) {
        case 'LIMIT_FILE_SIZE':
          errorMessage = '字体文件大小超过限制（40MB）';
          break;
        case 'LIMIT_UNEXPECTED_FILE':
          errorMessage = '无效的文件上传字段';
          break;
        default:
          errorMessage = `上传错误: ${error.message}`;
      }
    } else if (error instanceof Error) {
      errorMessage = error.message;
    }
    return sendError(res, errorMessage, true);
  }
};

// 删除WebUI字体文件处理方法
export const DeleteWebUIFontHandler: RequestHandler = async (_req, res) => {
  try {
    const fontPath = WebUiConfig.GetWebUIFontPath();
    const exists = await WebUiConfig.CheckWebUIFontExist();

    if (!exists) {
      return sendSuccess(res, true);
    }

    await fsProm.unlink(fontPath);
    return sendSuccess(res, true);
  } catch (_error) {
    return sendError(res, '删除字体文件失败');
  }
};
